package com.heima.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.message.vos.SearchArticleVo;
import com.heima.model.user.pojos.ApUser;
import com.heima.search.dtos.HistorySearchDto;
import com.heima.search.dtos.UserSearchDto;
import com.heima.search.pojos.ApUserSearch;
import com.heima.search.service.UserSearchService;
import com.heima.utils.threadLocal.ApUserThreadLocalUtil;
import com.mongodb.client.result.DeleteResult;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
public class UserSearchServiceImpl implements UserSearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private MongoTemplate mongoTemplate;
    /**
     * 搜索业务
     * @param dto
     * @return
     */
    @Override
    public ResponseResult search(UserSearchDto dto) {
        try {
        //1.执行搜索查询条件封装
            //1.2 创建SearchRequest对象，并且指定查询的索引库名称
        SearchRequest searchRequest=new SearchRequest("app_info_article");
            //1.3.1 创建一个布尔查询对象构建器
            BoolQueryBuilder queryBuilder= QueryBuilders.boolQuery();
            //1.3.1.1根据关键字查询
            if(dto.getSearchWords()==null){
                queryBuilder.filter(QueryBuilders.matchAllQuery());
            }else{
                queryBuilder.filter(QueryBuilders.matchQuery("all",dto.getSearchWords()));
            }
            //1.3.1.2 根据时间查询
            queryBuilder.filter(QueryBuilders.rangeQuery("publishTime").lte(dto.getMinBehotTime()));

            //1.3 往searchRequest对象添加查询条件
            searchRequest.source().query(queryBuilder);

            //1.4 分页设置
            searchRequest.source().from(dto.getPageNum()).size(dto.getPageSize());

            //1.5 时间倒叙查询
            searchRequest.source().sort("publishTime", SortOrder.DESC);

            //1.6高亮设置
            HighlightBuilder highlightBuilder=new HighlightBuilder();
            highlightBuilder.field("title");//指定高亮域  一般是标题
            highlightBuilder.preTags("<font style='color: red; font-size: inherit;'>");
            highlightBuilder.postTags("</font>");
            highlightBuilder.requireFieldMatch(false);
            searchRequest.source().highlighter(highlightBuilder);

        //1.1 调用search方法
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            //2.处理封装结果

            List list=new ArrayList();

            SearchHit[] hits = searchResponse.getHits().getHits();
            if(hits!=null && hits.length>0){
                for (SearchHit hit : hits) {
                    //获取source资源
                    Map<String, Object> sourceAsMap = hit.getSourceAsMap();

                    //开始进行高亮设置
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                    //表示有高亮域，则进行高亮处理
                    if(highlightFields.size()>0){
                        Text title = highlightFields.get("title").getFragments()[0];
                        //替换title
                        sourceAsMap.put("h_title",title.toString());
                    }else{
                        //没有高亮域
                        sourceAsMap.put("h_title",sourceAsMap.get("title"));
                    }
                    list.add(sourceAsMap);
                }
            }

            //异步调用保存历史记录的方法
            ApUser apUser = ApUserThreadLocalUtil.getApUser();
            Integer userId=apUser.getId();
            this.saveKeywordToMongo(dto.getSearchWords(),userId);


            //3.返回数据
            return ResponseResult.okResult(list);
        } catch (IOException e) {
            e.printStackTrace();
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"出现异常了");
        }
    }

    /**
     * 写入数据到es中
     *
     * @param vo
     */
    @Override
    public void writerArticleToEs(SearchArticleVo vo) {
        try {
        //2.创建IndexRequest，并且要指定索引名称
        IndexRequest indexRequest=new IndexRequest("app_info_article");
        //3.给索引id赋值
        indexRequest.id(vo.getId().toString());
        //4.写入数据到es
        indexRequest.source(JSON.toJSONString(vo), XContentType.JSON);
        //1.调用index方法，写入数据
            restHighLevelClient.index(indexRequest,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 保存用户搜索历史记录
     *
     * @param keyword
     * @param userId
     */
    @Override
    @Async
    public void saveKeywordToMongo(String keyword, Integer userId) {
        if(keyword==null || keyword.equals("")){
            return;
        }
        //1.根据关键字和用户id查询当前历史记录数据是否存在
        Query query=Query.query(Criteria.where("keyword").is(keyword).and("userId").is(userId));
        ApUserSearch apUserSearch = mongoTemplate.findOne(query, ApUserSearch.class);
        //2.如果存在，则更新时间
        if(apUserSearch!=null){
            apUserSearch.setCreatedTime(new Date());
            mongoTemplate.save(apUserSearch);
            return;
        }

        //3.如果不存在，判断当前用户下的历史记录条数是否大于10条
        List<ApUserSearch> searchList = mongoTemplate.find(Query.query(
                Criteria.where("userId").is(userId)
                ).with(Sort.by(Sort.Direction.DESC,"createdTime")),
                ApUserSearch.class);

        //3.5 封装新数据
        apUserSearch=new ApUserSearch();
        apUserSearch.setUserId(userId);
        apUserSearch.setKeyword(keyword);
        apUserSearch.setCreatedTime(new Date());

        //4.如果小于10条，则直接新增
        if(searchList!=null && searchList.size()<10){
            mongoTemplate.insert(apUserSearch);
        }else
            //5.如果大于等于10条，则替换最后一条数据
            if(searchList!=null && searchList.size()>=10){
                //1.获取最后一条数据
                ApUserSearch lastApUserSearch = searchList.get(searchList.size() - 1);
                /**
                 * 替换
                 * Query.query(Criteria.where("id").is(lastApUserSearch.getId())) 表示根据最后一条数据的id去查询mongo,返回是一个实体
                 * apUserSearch表示要替换的新数据
                 */
                mongoTemplate.findAndReplace(Query.query(
                        Criteria.where("id").is(lastApUserSearch.getId())
                ),apUserSearch);
            }
    }

    /**
     * 查询历史记录
     *
     * @return
     */
    @Override
    public ResponseResult findUserSearchHistory() {
        List<ApUserSearch> apUserSearchList = mongoTemplate.find(Query.query(
                Criteria.where("userId").is(ApUserThreadLocalUtil.getApUser().getId())
                ).with(Sort.by(Sort.Direction.DESC, "createdTime")),
                ApUserSearch.class);
        return ResponseResult.okResult(apUserSearchList);
    }

    /**
     * 删除历史记录
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult delApUserSerachHistory(HistorySearchDto dto) {
        DeleteResult result = mongoTemplate.remove(Query.query(
                Criteria.where("id").is(dto.getId())
                ),
                ApUserSearch.class);
        boolean flag = result.wasAcknowledged();
        if(flag){
            return ResponseResult.okResult("删除成功");
        }
        return ResponseResult.okResult("删除失败");
    }


}
